unit glx;

{
  == gl get err code
  1280 GL_INVALID_ENUM
  1281 GL_INVALID_VALUE
  1282 GL_INVALID_OPERATION
  1283 GL_STACK_OVERFLOW
  1284 GL_STACK_UNDERFLOW
  1285 GL_OUT_OF_MEMORY
}
interface

uses
  types,
  cls,
  enums,
  math;

  // msgGlCode
const
  GLVENDOR = $1f00;
  GLRENDERER = $1f01;
  GLVERSION = $1f02;
  GLEXTENSIONS = $1f03;
// gl code function
  glLines = $0001;
  glTris = $0004;
  glQuads = $0007;
// matrix ( !! mode !! )
  glModelViewMode = $1700;
  glProjViewMode = $1701;
// matrix( !! get !! )
  glGetProjMatrix = $0ba7;
// buffer
  glBuffDepthBit = $0100;
  glBuffColorBit = $4000;

// lib
const
  msgErrApiGlFunc : pchar = 'Fail create gl functionality';
  libGl32 = 'opengl32.dll';
  charNoUse : char = '#';
  glCntFn = { get } 5 + { set } 6 +
    { matrix } 2 + { draw } 3 + { vertex } 3 + { normal } 2 +
    { color } 1 + { attribute } 4;

type
// structApiTask

  structApiTask = packed record
    apifunc : enumApiFunc;
    case int of
     0 :
     ( pos : vec3 );
     1 :
     ( view : vec4i );
  end;

// void
  pStructApiTask = ^structApiTask;

// std:: structGLApi
  structGLApi = record
    case bool of
     false :
     ( names : array [ 1 .. glCntFn ] of pchar );
     true :
     (
// get        (3)
     get : record
//
     proc : function( procName : pchar ) : void stdcall;
     error : function : enum stdcall;
     str : function( name : enum ) : pchar stdcall;
     int : procedure( pname : enum; params : pint )stdcall;
     float : procedure( pname : enum; params : pfloat )stdcall;
     end;
//
// set        (5)
//
     &set : record
     //
     enable : procedure( cap : enum )stdcall;
     disable : procedure( cap : enum )stdcall;
     view : procedure( x, y, width, height : int )stdcall;
     // light
     light : record
     // params
     params : procedure( light, pname : enum; params : pvec4 )stdcall;
     end;
     // vsync
     vsync : record
     // vsync
     turn : function( mode : int ) : bool stdcall;
     get : function : int stdcall;
     end;
     end;
//
// matrix     (2)
//
     matrix : record
//
     mode : procedure( mode : enum )stdcall;
     load : procedure( const m : pmat4 )stdcall;
     end;
//
// draw       (3)
//
     draw : record
//
     clear : procedure( mask : enum )stdcall;
     &on : procedure( mode : enum )stdcall;
     off : procedure stdcall;
     end;
//
// vertex     (3)
//
     vertex : record
// v
     v2fv : procedure( const data : pvec3 )stdcall;
     v3fv : procedure( const data : pvec3 )stdcall;
     ptr : procedure( size : int; intype : enum; stride : int; const data : void )stdcall;
     end;
//
// normal     (3)
//
     normal : record
// v
     n3fv : procedure( const data : pvec3 )stdcall;
     ptr : procedure( size : int; intype : enum; stride : int; const data : void )stdcall;
     end;
     // color      (1)
     color : record
// v
     c3fv : procedure( const data : pvec3 )stdcall;
     end;
     // attribute  (4)
     attrib : record
//
     get : function( programObj : uint; char : pchar ) : int stdcall;
     &on : procedure( index : uint )stdcall;
     off : procedure( index : uint )stdcall;
     ptr : procedure( index : uint; size : int; intype : enum; normalized : bool; stride : int; const data : pfloat )stdcall;
     end; );
  end;

// void
  pStructGLApi = ^structGLApi;

// wgl(s)
// struct::wgl
  structWgl = record
    case bool of
     false :
     ( names : array [ 1 .. 3 ] of pchar );
     true :
     ( createCnt : function( const dc : handle ) : handle stdcall;
     makeCnt : function( const dc, hrc : handle ) : bool stdcall;
     deleteCnt : function( const hrc : handle ) : bool stdcall; );
  end;

    // std::errorGlStatus
  enumErrorGlStatus = ( errCreateCnt, errDraw );
  enumOfErrorGlStatus = set of enumErrorGlStatus;

// simpleGLClass
  CGLCore = class
  private
    fdc, fhrc : handle; // os, handled
    fstatus : enumOfErrorGlStatus;
    flibGl : handle;
    // fps
    fFps, fFpsAverage, fFpsTime : int64;
// case
  public
// CGLCore::Create
    constructor Create( const inLib : handle );
// CGLCore::initPixelFormat
    procedure initPixelFormat( const inWnd : handle );
// CGLCore::initGlFunctional
    function initGlFunctional( const inBuffer; const inPrintNotifier : CObject ) : bool;
// CGLCore::swap
    procedure swap;
// CGLCore::freeRender
    procedure freeRender;
// CGLCore::initGl3
    procedure initGl3( const inApi : void );
// CGLCore::processcmd
    procedure processcmd( const inTask : pStructApiTask; const inApi : pStructGLApi );
  end;

// struct::wgl::names
const
  wglApi : structWgl =
// names api function
   ( names : ( 'wglCreateContext', 'wglMakeCurrent', 'wglDeleteContext' { 'wglCreateContextAttribsARB' } ) );

implementation

uses
  os,
  utils,
  msg;

{ CGLCore }

// CGLCore::constructor
constructor CGLCore.Create( const inLib : handle );
var
  j : int;
begin
  fFps := 0;
  fFpsAverage := 0;
  fFpsTime := osGetTime;
  fstatus := [ ];
  flibGl := inLib;
// init wgl
  for j := 1 to system.length( wglApi.names ) do
    pvoid( @wglApi.names[ j ] )^ := osGetProc( flibGl, wglApi.names[ j ] );
end;

// CGLCore::freeRender
procedure CGLCore.freeRender;
begin
// del context
  wglApi.makeCnt( nullInt, nullInt );
// set zero cnt
  wglApi.deleteCnt( fhrc );
// free lib
  osLibFree( flibGl );
// free is class
  free;
end;

// CGLCore::initGlFunctional
function CGLCore.initGlFunctional( const inBuffer; const inPrintNotifier : CObject ) : bool;
var
  glfunctionlity : structGLApi absolute inBuffer;
  p : void;
  i : int;
// gl log
const
  glLog : array [ nullInt .. 3 ] of ansistring = ( 'vendor : ', 'render : ', 'version: ', 'ext: ' );
begin
  for i := 1 to system.length( glfunctionlity.names ) do
    if glfunctionlity.names[ i ] <> charNoUse then
    begin
     p := osGetProc( flibGl, glfunctionlity.names[ i ] );
     // check state 2.
     if p = nil then
     p := glfunctionlity.get.proc( glfunctionlity.names[ i ] );
     // check state 3. fail = error
     if p = nil then
     begin
     if inPrintNotifier <> nil then
     inPrintNotifier.printf( logWarn, ansistring( msgWarnDllNotFindProc ) + glfunctionlity.names[ i ] );
     exception( false, ansistring( msgWarnDllNotFindProc ) + glfunctionlity.names[ i ] );
     end;
     pvoid( @glfunctionlity.names[ i ] )^ := p;
    end;
     // print info
  for i := nullInt to system.length( glLog ) - 1 do
    inPrintNotifier.printf( logText, glLog[ i ] + glfunctionlity.get.str( GLVENDOR + i ), [ logLowcase ] ); // send to log
     // exit if done initialization then true
  result := true;
end;

// CGLCore::initPixelFormat
procedure CGLCore.initPixelFormat( const inWnd : handle );
var
  pxChoose : int;
  p : void;
const
  setCw = $133f;
  glNotSupported : ansistring = 'opengl $ver is not supported';
  failPxGet : ansistring = 'failed to find a suitable pixel format';
  failHrcGet : ansistring = 'failed to create an tempererary opengl rendering context';
  failPxSet : ansistring = 'failed to set the pixel format';
  failHrcActive : ansistring = 'failed to activate tempererary opengl rendering context';
begin
// get dc
  fdc := osDcGet( inWnd );
  pxChoose := osChoosePxFmt( fdc, 32, 16, p ); // todo: read params in xml-cfg
  exception( pxChoose <> nullInt, failPxGet );
  exception( osSetPxFmt( fdc, pxChoose, p ), failPxSet );
// get hrc
  fhrc := wglApi.createCnt( fdc );
  exception( fhrc <> nullInt, failHrcGet );
  exception( wglApi.makeCnt( fdc, fhrc ), failHrcActive );
// init opengl::load proc
  set8087cw( setCw );
end;

// CGLCore::processcmd
procedure CGLCore.processcmd( const inTask : pStructApiTask; const inApi : pStructGLApi );
begin
  with inTask^ do
    case apifunc of
     apifnSetView :
// set view port by parmetrs
     inApi.&set.view( view.pos.x, view.pos.y, view.size.width, view.pos.height );
    end;
end;

// CGLCore::swap
procedure CGLCore.swap;
begin
// swap backbuffer-frontbuffer
  osDcSwap( fdc );
end;

// CGLCore::initGl3
procedure CGLCore.initGl3( const inApi : void );
const
  gl3NotSupported : ansistring = 'opengl 3.0 is not supported';
  glContext : ansistring = 'wglCreateContextAttribsARB';
  failPxGet : ansistring = 'failed to find a suitable pixel format';
  failHrcGet : ansistring = 'failed to create an tempererary opengl rendering context';
  failPxSet : ansistring = 'failed to set the pixel format';
  failHrcActive : ansistring = 'failed to activate tempererary opengl rendering context';
  failHrc3Active : ansistring = 'failed to activate opengl rendering context';
  failDeleteCnt : ansistring = 'failed to delete context';

// wglArbCreateContext
  wglContextDebugBitArb = $0001;
  wglContextForwardCompatibleBitArb = $0002;
  wglContextMajorVersionArb = $2091;
  wglContextMinorVersionArb = $2092;
  wglContextLayerPlaneArb = $2093;
  wglContextFlagsArb = $2094;
  errorInvalidVersionArb = $2095;

  gl3FmtAttrib : array [ nullInt .. 7 ] of int = ( wglContextMajorVersionArb, 3, wglContextMinorVersionArb, nullInt, wglContextFlagsArb,
    wglContextForwardCompatibleBitArb, nullInt, nullInt );
var
  opengl3cnt : function( const dc, share : handle; const attrib : pint ) : handle stdcall;
  tempCnt : handle;
begin
// get gl3 init proc
  @opengl3cnt := structGLApi( inApi^ ).get.proc( pchar( glContext ) );
  exception( @opengl3cnt <> nil, gl3NotSupported );
// init gl3 -> to temp context                 // share null!
  tempCnt := opengl3cnt( fdc, nullInt, @gl3FmtAttrib );
// make new context
  exception( wglApi.makeCnt( fdc, tempCnt ), failHrc3Active );
// delete temp context
  exception( wglApi.deleteCnt( fhrc ), failDeleteCnt );
  fhrc := tempCnt;
end;

end.
